`include "define.v"

module ex (
    input wire rst,

    //译码阶段送到执行阶段的信息
    input wire[`AluOpBus] aluop_i,
    input wire[`AluSelBus] alusel_i,
    input wire[`RegBus] reg1_data_i,
    input wire[`RegBus] reg2_data_i,
    input wire[`RegAddrBus] write_reg_addr_i,
    input wire write_reg_enable_i,

    input wire[`DoubleRegBus]     div_result_i,
	input wire                    div_ready_i,
    input wire[31:0]              excepttype_i,
	input wire[`RegBus]          current_inst_address_i,

    //访存阶段的hi,lo
    input wire[`RegBus] mem_hi_i,
    input wire[`RegBus] mem_lo_i,
    input wire mem_hilo_write_enable_i,

    //回写阶段的hi,lo
    input wire[`RegBus] wb_hi_i,
    input wire[`RegBus] wb_lo_i,
    input wire wb_hilo_write_enable_i,

    //HILO模块给出的HI,LO寄存器的值
    input wire[`RegBus] hi_i,
    input wire[`RegBus] lo_i,

    input wire[`DoubleRegBus] hilo_temp_i,
    input wire[1:0] cnt_i,

    input wire[`RegBus]           link_address_i,
	input wire                    is_in_delayslot_i,
    input wire[`RegBus] inst_i,

    input wire                    mem_cp0_reg_we,
	input wire[4:0]               mem_cp0_reg_write_addr,
	input wire[`RegBus]           mem_cp0_reg_data,

    input wire                    wb_cp0_reg_we,
	input wire[4:0]               wb_cp0_reg_write_addr,
	input wire[`RegBus]           wb_cp0_reg_data,

    input wire[`RegBus]           cp0_reg_data_i,
	output reg[4:0]               cp0_reg_read_addr_o,

    output reg                    cp0_reg_we_o,
	output reg[4:0]               cp0_reg_write_addr_o,
	output reg[`RegBus]           cp0_reg_data_o,

    //执行的结果
    output reg[`RegAddrBus] write_reg_addr_o,
    output reg write_reg_enable_o,
    output reg[`RegBus] write_reg_data_o,
    output reg[`RegBus] hi_o,
    output reg[`RegBus] lo_o,
    output reg    whilo_o,

    output reg[`DoubleRegBus] hilo_temp_o,
    output reg[1:0] cnt_o,

    output reg[`RegBus]           div_opdata1_o,
	output reg[`RegBus]           div_opdata2_o,
	output reg                    div_start_o,
	output reg                    signed_div_o,

    output wire[`AluOpBus]        aluop_o,
	output wire[`RegBus]          mem_addr_o,
	output wire[`RegBus]          reg2_data_o,
	output wire[31:0]             excepttype_o,
	output wire                   is_in_delayslot_o,
    output wire[`RegBus]          current_inst_address_o,

    output reg stallreq
);
    reg[`RegBus] logicout;
    reg[`RegBus] shiftres;
    reg[`RegBus] moveres;
    reg[`RegBus] HI;
    reg[`RegBus] LO;
    reg[`RegBus] arithmeticres;


    wire[`RegBus] reg2_data_i_mux;
	wire[`RegBus] reg1_data_i_not;	
	wire[`RegBus] result_sum;
	wire ov_sum;
	wire reg1_eq_reg2;
	wire reg1_lt_reg2;
	wire[`RegBus] opdata1_mult;
	wire[`RegBus] opdata2_mult;
	wire[`DoubleRegBus] hilo_temp;
    reg[`DoubleRegBus] hilo_temp1;
    reg[`DoubleRegBus] mulres;

    reg stallreq_for_madd_msub;			
	reg stallreq_for_div;
    reg ovassert;
    reg trapassert;

    assign reg2_data_i_mux = ((aluop_i == `EXE_SUB_OP) || (aluop_i == `EXE_SUBU_OP) ||
											 (aluop_i == `EXE_SLT_OP)|| (aluop_i == `EXE_TLT_OP) ||
	                       (aluop_i == `EXE_TLTI_OP) || (aluop_i == `EXE_TGE_OP) ||
	                       (aluop_i == `EXE_TGEI_OP)) 
											 ? (~reg2_data_i)+1 : reg2_data_i;

    assign result_sum = reg1_data_i + reg2_data_i_mux;

    //判断加减法指令是否有溢出，下面两种情况算是溢出：
    //1. reg1_data_i为正数， reg2_data_i_mux为正数，但两者之和为负数。
    //2. reg1_data_i和reg2_data_i_mux 为负数，但两者之和为正数。
    assign ov_sum = ((!reg1_data_i[31] && !reg2_data_i_mux[31]) && result_sum[31]) ||
								((reg1_data_i[31] && reg2_data_i_mux[31]) && (!result_sum[31])); 

    //计算操作数1 是否小于 操作数2
    assign reg1_lt_reg2 = ((aluop_i == `EXE_SLT_OP) || (aluop_i == `EXE_TLT_OP) ||
	                       (aluop_i == `EXE_TLTI_OP) || (aluop_i == `EXE_TGE_OP) ||
	                       (aluop_i == `EXE_TGEI_OP)) ?
												 ((reg1_data_i[31] && !reg2_data_i[31]) || 
												 (!reg1_data_i[31] && !reg2_data_i[31] && result_sum[31])||
			                   (reg1_data_i[31] && reg2_data_i[31] && result_sum[31]))
			                   :	(reg1_data_i < reg2_data_i);

    assign reg1_data_i_not = ~reg1_data_i;

    assign aluop_o = aluop_i;

    assign mem_addr_o = reg1_data_i + {{16{inst_i[15]}}, inst_i[15:0]};
    assign reg2_data_o = reg2_data_i;


    always @ (*) begin
        stallreq = stallreq_for_madd_msub || stallreq_for_div;
    end

    assign excepttype_o = {excepttype_i[31:12],ovassert,trapassert,excepttype_i[9:8],8'h00};
  
	assign is_in_delayslot_o = is_in_delayslot_i;
	assign current_inst_address_o = current_inst_address_i;

	always @ (*) begin
		if(rst == `RstEnable) begin
			trapassert <= `TrapNotAssert;
		end else begin
			trapassert <= `TrapNotAssert;
			case (aluop_i)
				`EXE_TEQ_OP, `EXE_TEQI_OP:		begin
					if( reg1_data_i == reg2_data_i ) begin
						trapassert <= `TrapAssert;
					end
				end
				`EXE_TGE_OP, `EXE_TGEI_OP, `EXE_TGEIU_OP, `EXE_TGEU_OP:		begin
					if( ~reg1_lt_reg2 ) begin
						trapassert <= `TrapAssert;
					end
				end
				`EXE_TLT_OP, `EXE_TLTI_OP, `EXE_TLTIU_OP, `EXE_TLTU_OP:		begin
					if( reg1_lt_reg2 ) begin
						trapassert <= `TrapAssert;
					end
				end
				`EXE_TNE_OP, `EXE_TNEI_OP:		begin
					if( reg1_data_i != reg2_data_i ) begin
						trapassert <= `TrapAssert;
					end
				end
				default:				begin
					trapassert <= `TrapNotAssert;
				end
			endcase
		end
	end

    always @ (*) begin
        if (rst == `RstEnable) begin
            logicout <= `ZeroWord;
        end else begin
            case (aluop_i)
                `EXE_SLT_OP, `EXE_SLTU_OP:		begin
					arithmeticres <= reg1_lt_reg2 ;
				end
				`EXE_ADD_OP, `EXE_ADDU_OP, `EXE_ADDI_OP, `EXE_ADDIU_OP:		begin
					arithmeticres <= result_sum; 
				end
				`EXE_SUB_OP, `EXE_SUBU_OP:		begin
					arithmeticres <= result_sum; 
				end		
				`EXE_CLZ_OP:		begin
					arithmeticres <= reg1_data_i[31] ? 0 : reg1_data_i[30] ? 1 : reg1_data_i[29] ? 2 :
													 reg1_data_i[28] ? 3 : reg1_data_i[27] ? 4 : reg1_data_i[26] ? 5 :
													 reg1_data_i[25] ? 6 : reg1_data_i[24] ? 7 : reg1_data_i[23] ? 8 : 
													 reg1_data_i[22] ? 9 : reg1_data_i[21] ? 10 : reg1_data_i[20] ? 11 :
													 reg1_data_i[19] ? 12 : reg1_data_i[18] ? 13 : reg1_data_i[17] ? 14 : 
													 reg1_data_i[16] ? 15 : reg1_data_i[15] ? 16 : reg1_data_i[14] ? 17 : 
													 reg1_data_i[13] ? 18 : reg1_data_i[12] ? 19 : reg1_data_i[11] ? 20 :
													 reg1_data_i[10] ? 21 : reg1_data_i[9] ? 22 : reg1_data_i[8] ? 23 : 
													 reg1_data_i[7] ? 24 : reg1_data_i[6] ? 25 : reg1_data_i[5] ? 26 : 
													 reg1_data_i[4] ? 27 : reg1_data_i[3] ? 28 : reg1_data_i[2] ? 29 : 
													 reg1_data_i[1] ? 30 : reg1_data_i[0] ? 31 : 32 ;
				end
				`EXE_CLO_OP:		begin
					arithmeticres <= (reg1_data_i_not[31] ? 0 : reg1_data_i_not[30] ? 1 : reg1_data_i_not[29] ? 2 :
													 reg1_data_i_not[28] ? 3 : reg1_data_i_not[27] ? 4 : reg1_data_i_not[26] ? 5 :
													 reg1_data_i_not[25] ? 6 : reg1_data_i_not[24] ? 7 : reg1_data_i_not[23] ? 8 : 
													 reg1_data_i_not[22] ? 9 : reg1_data_i_not[21] ? 10 : reg1_data_i_not[20] ? 11 :
													 reg1_data_i_not[19] ? 12 : reg1_data_i_not[18] ? 13 : reg1_data_i_not[17] ? 14 : 
													 reg1_data_i_not[16] ? 15 : reg1_data_i_not[15] ? 16 : reg1_data_i_not[14] ? 17 : 
													 reg1_data_i_not[13] ? 18 : reg1_data_i_not[12] ? 19 : reg1_data_i_not[11] ? 20 :
													 reg1_data_i_not[10] ? 21 : reg1_data_i_not[9] ? 22 : reg1_data_i_not[8] ? 23 : 
													 reg1_data_i_not[7] ? 24 : reg1_data_i_not[6] ? 25 : reg1_data_i_not[5] ? 26 : 
													 reg1_data_i_not[4] ? 27 : reg1_data_i_not[3] ? 28 : reg1_data_i_not[2] ? 29 : 
													 reg1_data_i_not[1] ? 30 : reg1_data_i_not[0] ? 31 : 32) ;
				end
				default:				begin
					arithmeticres <= `ZeroWord;
				end
            endcase
        end
    end

    //执行乘法指令，如果reg1 或 reg2为负数，则取对应的补码
	assign opdata1_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MADD_OP) || (aluop_i == `EXE_MSUB_OP))
													&& (reg1_data_i[31] == 1'b1)) ? (~reg1_data_i + 1) : reg1_data_i;

    assign opdata2_mult = (((aluop_i == `EXE_MUL_OP) || (aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MADD_OP) || (aluop_i == `EXE_MSUB_OP))
													&& (reg2_data_i[31] == 1'b1)) ? (~reg2_data_i + 1) : reg2_data_i;		

    assign hilo_temp = opdata1_mult * opdata2_mult;																				

    //对于乘法结果进行修正，如果是有符号乘法，则判断乘数与被乘数符号是否相同，如果不相同取结果的补码。反之取hilo_temp原值
	always @ (*) begin
		if(rst == `RstEnable) begin
			mulres <= {`ZeroWord,`ZeroWord};
		end else if ((aluop_i == `EXE_MULT_OP) || (aluop_i == `EXE_MUL_OP)||
									(aluop_i == `EXE_MADD_OP) || (aluop_i == `EXE_MSUB_OP))begin
			if(reg1_data_i[31] ^ reg2_data_i[31] == 1'b1) begin
				mulres <= ~hilo_temp + 1;
			end else begin
			    mulres <= hilo_temp;
			end
		end else begin
				mulres <= hilo_temp;
		end
	end

    //触发指令
    always @ (*) begin
		if(rst == `RstEnable) begin
			stallreq_for_div <= `NoStop;
	        div_opdata1_o <= `ZeroWord;
			div_opdata2_o <= `ZeroWord;
			div_start_o <= `DivStop;
			signed_div_o <= 1'b0;
		end else begin
			stallreq_for_div <= `NoStop;
	        div_opdata1_o <= `ZeroWord;
			div_opdata2_o <= `ZeroWord;
			div_start_o <= `DivStop;
			signed_div_o <= 1'b0;	
			case (aluop_i) 
				`EXE_DIV_OP:		begin
					if(div_ready_i == `DivResultNotReady) begin
	    			    div_opdata1_o <= reg1_data_i;
						div_opdata2_o <= reg2_data_i;
						div_start_o <= `DivStart;
						signed_div_o <= 1'b1;
						stallreq_for_div <= `Stop;
					end else if(div_ready_i == `DivResultReady) begin
	    			    div_opdata1_o <= reg1_data_i;
						div_opdata2_o <= reg2_data_i;
						div_start_o <= `DivStop;
						signed_div_o <= 1'b1;
						stallreq_for_div <= `NoStop;
					end else begin						
	    			    div_opdata1_o <= `ZeroWord;
						div_opdata2_o <= `ZeroWord;
						div_start_o <= `DivStop;
						signed_div_o <= 1'b0;
						stallreq_for_div <= `NoStop;
					end					
				end
				`EXE_DIVU_OP:		begin
					if(div_ready_i == `DivResultNotReady) begin
	    			    div_opdata1_o <= reg1_data_i;
						div_opdata2_o <= reg2_data_i;
						div_start_o <= `DivStart;
						signed_div_o <= 1'b0;
						stallreq_for_div <= `Stop;
					end else if(div_ready_i == `DivResultReady) begin
	    			    div_opdata1_o <= reg1_data_i;
						div_opdata2_o <= reg2_data_i;
						div_start_o <= `DivStop;
						signed_div_o <= 1'b0;
						stallreq_for_div <= `NoStop;
					end else begin						
	    			    div_opdata1_o <= `ZeroWord;
						div_opdata2_o <= `ZeroWord;
						div_start_o <= `DivStop;
						signed_div_o <= 1'b0;
						stallreq_for_div <= `NoStop;
					end					
				end
				default: begin
				end
			endcase
		end
	end	

    always @ (*) begin
		if(rst == `RstEnable) begin
			hilo_temp_o <= {`ZeroWord,`ZeroWord};
			cnt_o <= 2'b00;
			stallreq_for_madd_msub <= `NoStop;
		end else begin
			
			case (aluop_i) 
				`EXE_MADD_OP, `EXE_MADDU_OP:		begin
					if(cnt_i == 2'b00) begin
						hilo_temp_o <= mulres;
						cnt_o <= 2'b01;
						stallreq_for_madd_msub <= `Stop;
						hilo_temp1 <= {`ZeroWord,`ZeroWord};
					end else if(cnt_i == 2'b01) begin
						hilo_temp_o <= {`ZeroWord,`ZeroWord};						
						cnt_o <= 2'b10;
						hilo_temp1 <= hilo_temp_i + {HI,LO};
						stallreq_for_madd_msub <= `NoStop;
					end
				end
				`EXE_MSUB_OP, `EXE_MSUBU_OP:		begin
					if(cnt_i == 2'b00) begin
						hilo_temp_o <=  ~mulres + 1 ;
						cnt_o <= 2'b01;
						stallreq_for_madd_msub <= `Stop;
                        hilo_temp1 <= {`ZeroWord,`ZeroWord};
					end else if(cnt_i == 2'b01)begin
						hilo_temp_o <= {`ZeroWord,`ZeroWord};						
						cnt_o <= 2'b10;
						hilo_temp1 <= hilo_temp_i + {HI,LO};
						stallreq_for_madd_msub <= `NoStop;
					end				
				end
				default:	begin
					hilo_temp_o <= {`ZeroWord,`ZeroWord};
					cnt_o <= 2'b00;
					stallreq_for_madd_msub <= `NoStop;				
				end
			endcase
		end
	end	

    always @ (*) begin
        if (rst == `RstEnable) begin
            logicout <= `ZeroWord;
        end else begin
            case (aluop_i)
                `EXE_OR_OP: begin
                    logicout <= reg1_data_i | reg2_data_i;
                end
                `EXE_AND_OP:		begin
					logicout <= reg1_data_i & reg2_data_i;
				end
				`EXE_NOR_OP:		begin
					logicout <= ~(reg1_data_i |reg2_data_i);
				end
				`EXE_XOR_OP:		begin
					logicout <= reg1_data_i ^ reg2_data_i;
				end
                default:  begin
                    logicout <= `ZeroWord;
                end
            endcase
        end
    end

    always @ (*) begin
		if(rst == `RstEnable) begin
			shiftres <= `ZeroWord;
		end else begin
			case (aluop_i)
				`EXE_SLL_OP:			begin
					shiftres <= reg2_data_i << reg1_data_i[4:0] ;
				end
				`EXE_SRL_OP:		begin
					shiftres <= reg2_data_i >> reg1_data_i[4:0];
				end
				`EXE_SRA_OP:		begin
					shiftres <= ({32{reg2_data_i[31]}} << (6'd32-{1'b0, reg1_data_i[4:0]})) 
												| reg2_data_i >> reg1_data_i[4:0];
				end
				default:				begin
					shiftres <= `ZeroWord;
				end
			endcase
		end    //if
	end      //always

    //处理hi 和 lo寄存器的流水线数据相关
    always @(*) begin
        if (rst == `RstEnable) begin
            HI <= `ZeroWord;
            LO <= `ZeroWord;
        end else if (mem_hilo_write_enable_i == `WriteEnable) begin
            HI <= mem_hi_i;
            LO <= mem_lo_i;
        end else if (wb_hilo_write_enable_i == `WriteEnable) begin
            HI <= wb_hi_i;
            LO <= wb_lo_i;
        end else begin
            HI <= hi_i;
            LO <= lo_i;
        end
    end

    always @ (*) begin
        if(rst == `RstEnable) begin
            moveres <= `ZeroWord;
        end else begin
            moveres <= `ZeroWord;
            case (aluop_i)
                `EXE_MFHI_OP: begin
                    moveres <= HI;
                end 
                `EXE_MFLO_OP: begin
                    moveres <= LO;
                end
                `EXE_MOVZ_OP: begin
                    moveres <= reg1_data_i;
                end
                `EXE_MOVN_OP: begin
                    moveres <= reg1_data_i;
                end
                `EXE_MFC0_OP:		begin
                    cp0_reg_read_addr_o <= inst_i[15:11];
                    moveres <= cp0_reg_data_i;
                    if( mem_cp0_reg_we == `WriteEnable &&
                            mem_cp0_reg_write_addr == inst_i[15:11] ) begin
                            moveres <= mem_cp0_reg_data;
                    end else if( wb_cp0_reg_we == `WriteEnable &&
                                                        wb_cp0_reg_write_addr == inst_i[15:11] ) begin
                            moveres <= wb_cp0_reg_data;
                    end
                end
                default: begin
                end
            endcase
        end
    end

    always @(*) begin
        write_reg_addr_o <= write_reg_addr_i;

        if(((aluop_i == `EXE_ADD_OP) || (aluop_i == `EXE_ADDI_OP) || 
            (aluop_i == `EXE_SUB_OP)) && (ov_sum == 1'b1)) begin
	 	    write_reg_enable_o <= `WriteDisable;
            ovassert <= 1'b1;
	    end else begin
	        write_reg_enable_o <= write_reg_enable_i;
            ovassert <= 1'b0;
	    end

        case (alusel_i)
            `EXE_RES_LOGIC:     begin
                write_reg_data_o <= logicout;
            end 
            `EXE_RES_SHIFT: begin
                write_reg_data_o <= shiftres;
            end
            `EXE_RES_MOVE: begin
                write_reg_data_o <= moveres;
            end
            `EXE_RES_ARITHMETIC:	begin
                write_reg_data_o <= arithmeticres;
            end
            `EXE_RES_MUL:		begin
                write_reg_data_o <= mulres[31:0];
            end
            `EXE_RES_JUMP_BRANCH:	begin
                write_reg_data_o <= link_address_i;
            end	 	
            default:    begin
                write_reg_data_o <= `ZeroWord;
            end
        endcase
    end

    always @(*) begin
        if(rst == `RstEnable) begin
            whilo_o <= `WriteDisable;
            hi_o <= `ZeroWord;
            lo_o <= `ZeroWord;
        end else begin
            case (aluop_i)
                `EXE_MTHI_OP: begin
                    whilo_o <= `WriteEnable;
                    hi_o <= reg1_data_i;
                    lo_o <= LO;
                end
                `EXE_MULT_OP, `EXE_MULTU_OP: begin
                    whilo_o <= `WriteEnable;
                    hi_o <= mulres[63:32];
                    lo_o <= mulres[31:0];
                end
                `EXE_MADD_OP, `EXE_MADDU_OP: begin
                    whilo_o <= `WriteEnable;
                    hi_o <= hilo_temp1[63:32];
                    lo_o <= hilo_temp1[31:0];
                end
                `EXE_MSUB_OP, `EXE_MSUBU_OP: begin
                    whilo_o <= `WriteEnable;
                    hi_o <= hilo_temp1[63:32];
                    lo_o <= hilo_temp1[31:0];
                end
                `EXE_MTLO_OP: begin
                    whilo_o <= `WriteEnable;
                    hi_o <= HI;
                    lo_o <= reg1_data_i;
                end
                `EXE_DIV_OP, `EXE_DIVU_OP: begin
                    whilo_o <= `WriteEnable;
                    hi_o <= div_result_i[63:32];
                    lo_o <= div_result_i[31:0];	
                end
                default: begin
                    whilo_o <= `WriteDisable;
                    hi_o <= `ZeroWord;
                    lo_o <= `ZeroWord;
                end
            endcase
        end
    end

    always @ (*) begin
		if(rst == `RstEnable) begin
			cp0_reg_write_addr_o <= 5'b00000;
			cp0_reg_we_o <= `WriteDisable;
			cp0_reg_data_o <= `ZeroWord;
		end else if(aluop_i == `EXE_MTC0_OP) begin
			cp0_reg_write_addr_o <= inst_i[15:11];
			cp0_reg_we_o <= `WriteEnable;
			cp0_reg_data_o <= reg1_data_i;
	  end else begin
			cp0_reg_write_addr_o <= 5'b00000;
			cp0_reg_we_o <= `WriteDisable;
			cp0_reg_data_o <= `ZeroWord;
		end				
	end		
endmodule
